<meta charset=utf-8>
<style>
body { margin: 0px; }
canvas { width:100%; height:100%; overflow: hidden; }
</style>
<head><meta name="viewport" content="user-scalable=no,initial-scale=1,maximum-scale=1">
<script type="text/javascript" src="../h3du_min.js"></script>
<script type="text/javascript" src="../extras/frame.js"></script>
<script type="text/javascript" src="demoutil.js"></script>
<script type="text/javascript" src="../extras/meshjson.js"></script>
<script type="text/javascript" src="../extras/polyhedra.js"></script>
</head>
<p style="position:absolute;left:0;top:1em">
<a href="javascript:link1()">Grayscale</a>,
<a href="javascript:link2()">Invert colors</a>,
<a href="javascript:link3()">Wave</a>,
<a href="javascript:link4()">Blur</a>,
<a href="javascript:link5()">Pixelate</a>,
<a href="javascript:link6()">Waterpaint</a>,
<a href="javascript:link7()">Mirror</a>,
<a href="javascript:link8()">Edge detect</a>,
<a href="javascript:link9()">Red Tint</a>,
<a href="javascript:link9a()">Blue Tint</a>,
<a href="javascript:link10()">Warp</a>,
<a href="javascript:linksepia1()">Sepia I</a>,
<a href="javascript:linksepia2()">Sepia II</a>,
<a href="javascript:linkvignette()">Vignette</a>,
<a href="javascript:linknone()">No filters</a>
<br>
<a href="javascript:link1a()">Blinds</a>,
<a href="javascript:link2a()">Platonic solids</a>
</p>
<canvas id=canvas></canvas>
<script id="demo">
/* global H3DU, Polyhedra */
// <!--
/*
 Any copyright to this file is released to the Public Domain.
 http://creativecommons.org/publicdomain/zero/1.0/
 If you like this, you should donate
 to Peter O. (original author of
 the Public Domain HTML 3D Library) at:
 http://peteroupc.github.io/
*/
var currentFilter = null;
var scene = null,
  renders = null,
  fbo = null,
  subScene = null,
  platonic = false,
  blinds = false;
function makeWaterpaint() {
  "use strict";
// Adapted from Themaister's Waterpaint shader
  return H3DU.ShaderInfo.makeEffect([
    "vec4 compress(vec4 in_color, float threshold, float ratio)",
    "{",
    "  vec4 diff = in_color - vec4(threshold);",
    "  diff = clamp(diff, vec4(0.0), vec4(100.0));",
    "  return in_color - (diff * (1.0 - 1.0/ratio));",
    "}",
    "vec4 textureEffect(sampler2D sampler, vec2 uvCoord, vec2 textureSize) {",
    " vec2 pixel=1.0/textureSize;",
    " vec2 xy0=uvCoord-pixel;",
    " xy0=vec2(max(xy0.x,0.0),max(xy0.y,0.0));",
    " vec2 xy1=uvCoord;",
    " vec2 xy2=uvCoord+pixel;",
    " xy2=vec2(min(xy2.x,1.0),min(xy2.y,1.0));",
    " vec4 mainColor=texture2D(sampler,uvCoord);",
    " vec4 c00=texture2D(sampler,xy0);",
    " vec4 c10=texture2D(sampler,vec2(xy1.x,xy0.y));",
    " vec4 c20=texture2D(sampler,vec2(xy2.x,xy0.y));",
    " vec4 c01=texture2D(sampler,vec2(xy0.x,xy1.y));",
    " vec4 c11=mainColor;",
    " vec4 c21=texture2D(sampler,vec2(xy2.x,xy1.y));",
    " vec4 c02=texture2D(sampler,vec2(xy0.x,xy2.y));",
    " vec4 c12=texture2D(sampler,vec2(xy1.x,xy2.y));",
    " vec4 c22=texture2D(sampler,xy2);",
    "  vec2 texsize = textureSize;",
    "  vec4 first = mix(c00, c20, fract(uvCoord.x * texsize.x + 0.5));",
    "  vec4 second = mix(c02, c22, fract(uvCoord.x * texsize.x + 0.5));",
    "  vec4 mid_horiz = mix(c01, c21, fract(uvCoord.x * texsize.x + 0.5));",
    "  vec4 mid_vert = mix(c10, c12, fract(uvCoord.y * texsize.y + 0.5));",
    "  vec4 res = mix(first, second, fract(uvCoord.y * texsize.y + 0.5));",
    "  vec4 final = 0.26 * (res + mid_horiz + mid_vert) + 3.5 * abs(res - mix(mid_horiz, mid_vert, 0.5));",
    "  final = compress(final, 0.8, 5.0);",
    "  final.a = mainColor.a;",
    "  return final;",
    "}"].join("\n"));
}

function makePixelate() {
  "use strict";
  return H3DU.ShaderInfo.makeEffect([
    "uniform float coarseness;", // coarseness in pixels; 1 means normal
    "vec4 textureEffect(sampler2D sampler, vec2 uvCoord, vec2 textureSize) {",
    " float g=max(coarseness,1.0);",
    " float gridSizeX=textureSize.x/g;",
    " float gridSizeY=textureSize.y/g;",
    " float uv0=floor(uvCoord.x*gridSizeX)/gridSizeX;",
    " float uv1=floor(uvCoord.y*gridSizeY)/gridSizeY;",
    " vec4 c=texture2D(sampler,vec2(uv0,uv1));",
    " return c;",
    "}"].join("\n"));
}

function makeWarp() {
  "use strict";
  return H3DU.ShaderInfo.makeEffect([
    "uniform float coarseness;", // coarseness in pixels; 1 means normal
    "vec4 textureEffect(sampler2D sampler, vec2 uvCoord, vec2 textureSize) {",
    " vec2 norm=uvCoord*2.0-1.0;",
    " float r=length(norm);",
    " vec2 unitnorm=(r==0.0) ? norm : (norm/r);",
    " r=sqrt(r);",
    " float x=((r*unitnorm.x)+1.0)*0.5;",
    " float y=((r*unitnorm.y)+1.0)*0.5;",
    " vec4 c=texture2D(sampler,vec2(x,y));",
    " return c;",
    "}"].join("\n"));
}

function makeColorMatrix() {
  "use strict";
  return H3DU.ShaderInfo.makeEffect([
    "uniform mat4 colorMatrix;",
    "uniform float t;", // ranges from 0 to 1
    "#define tolinear(c) pow(c, vec3(2.2))",
    "#define fromlinear(c) pow(c, vec3(0.45454545))",
    "vec4 textureEffect(sampler2D sampler, vec2 uvCoord, vec2 textureSize) {",
    " vec4 tex=texture2D(sampler,uvCoord);",
    " vec3 texRgb=tolinear(tex.rgb);",
    " vec4 color=colorMatrix*vec4(texRgb,1.0);",
    " vec3 colorRgb=mix(texRgb,color.rgb/color.a,t);",
    " colorRgb=fromlinear(colorRgb);",
    " return vec4(clamp(colorRgb,0.0,1.0),tex.a);",
    "}"].join("\n"));
}

function makeVignette() {
  "use strict";
  // Based on a public domain vignette shader by Landon Manning
  return H3DU.ShaderInfo.makeEffect([
    "uniform float radius;",
    "uniform float softness;",
    "uniform float opacity;",
    "#define tolinear(c) pow(c, vec3(2.2))",
    "#define fromlinear(c) pow(c, vec3(0.45454545))",
    "vec4 textureEffect(sampler2D sampler, vec2 uvCoord, vec2 textureSize) {",
    " vec4 tex=texture2D(sampler,uvCoord);",
    " tex.rgb=tolinear(tex.rgb);",
    " float vignette=smoothstep(radius,radius-softness,length(uvCoord-vec2(0.5)));",
    " vec3 color=mix(tex.rgb,tex.rgb*vignette,opacity);",
    " tex.rgb=fromlinear(color);",
    " return tex;",
    "}"].join("\n"));
}

function setKernelMatrix(program, matrix) {
  "use strict";
  var weight = matrix[0] + matrix[1] + matrix[2] +
   matrix[3] + matrix[4] + matrix[5] + matrix[6] +
   matrix[7] + matrix[8];
  if(weight <= 0)weight = 1.0;
  var mat = [];
 // Ensure the sum of each matrix
 // element is 1
  for(var i = 0; i < 9; i++) {
    mat[i] = matrix[i] / weight;
  }
  program.setUniforms({"matrix":mat});
}

function makeKernelMatrix() {
  "use strict";
  return H3DU.ShaderInfo.makeEffect([
    "uniform mat3 matrix;",
    "vec4 textureEffect(sampler2D sampler, vec2 uvCoord, vec2 textureSize) {",
    " vec2 pixel=1.0/textureSize;",
    " vec2 xy0=uvCoord-pixel;",
    " xy0=vec2(max(xy0.x,0.0),max(xy0.y,0.0));",
    " vec2 xy1=uvCoord;",
    " vec2 xy2=uvCoord+pixel;",
    " xy2=vec2(min(xy2.x,1.0),min(xy2.y,1.0));",
    " vec4 mainColor=texture2D(sampler,uvCoord);",
    " // NOTE: Assumes row-major matrices; e.g. [0][0] means ",
    " // index 0, and [0][1] means index 1",
    " vec4 x0y0=texture2D(sampler,xy0)*matrix[0][0];",
    " vec4 x1y0=texture2D(sampler,vec2(xy1.x,xy0.y))*matrix[0][1];",
    " vec4 x2y0=texture2D(sampler,vec2(xy2.x,xy0.y))*matrix[0][2];",
    " vec4 x0y1=texture2D(sampler,vec2(xy0.x,xy1.y))*matrix[1][0];",
    " vec4 x1y1=mainColor*matrix[1][1];",
    " vec4 x2y1=texture2D(sampler,vec2(xy2.x,xy1.y))*matrix[1][2];",
    " vec4 x0y2=texture2D(sampler,vec2(xy0.x,xy2.y))*matrix[2][0];",
    " vec4 x1y2=texture2D(sampler,vec2(xy1.x,xy2.y))*matrix[2][1];",
    " vec4 x2y2=texture2D(sampler,xy2)*matrix[2][2];",
    " vec4 color=x0y0+x1y0+x2y0+x0y1+x1y1+x2y1+x0y2+x1y2+x2y2;",
    " return vec4(color.rgb,mainColor.a);",
    "}"].join("\n"));
}

function makeWave() {
  "use strict";
  return H3DU.ShaderInfo.makeEffect([
    "uniform float time; // range [0,100)",
    "const float pi = 3.14159265;",
    "float interp(float t) {",
    " return sin(t*pi*2.0);",
    "}",
    "vec4 textureEffect(sampler2D sampler, vec2 uvCoord, vec2 textureSize) {",
    " float t=float(time)*0.01;",
    " t=t+uvCoord.y;",
    " float offset=interp(fract(t*8.0));",
    " float x=clamp(uvCoord.x+offset*0.02,0.0,1.0);",
    " vec4 color=texture2D(sampler,vec2(x,uvCoord.y));",
    " return color;",
    "}"].join("\n"));
}

function makeMirror() {
  "use strict";
  return H3DU.ShaderInfo.makeEffect([
    "vec4 textureEffect(sampler2D sampler, vec2 uvCoord, vec2 textureSize) {",
    " vec4 color=texture2D(sampler,vec2(1.0-uvCoord.x,uvCoord.y));",
    " return color;",
    "}"].join("\n"));
}

/* exported link1 */
function link1() {
  "use strict";
  if(scene) {
    currentFilter = makeColorMatrix();
    currentFilter.setUniforms({
      "colorMatrix":[
        1 / 3, 1 / 3, 1 / 3, 0,
        1 / 3, 1 / 3, 1 / 3, 0,
        1 / 3, 1 / 3, 1 / 3, 0,
        0, 0, 0, 1
      ]
    });
    renders[1] = new H3DU.RenderPass(H3DU.Batch3D.forFilter(scene, fbo, currentFilter));
  }
}

/* exported linksepia1 */
function linksepia1() {
  "use strict";
  if(scene) {
    currentFilter = makeColorMatrix();
    currentFilter.setUniforms({
      "colorMatrix":[
        0.3588, 0.299, 0.2392, 0,
        0.7044, 0.587, 0.4696, 0,
        0.1368, 0.114, 0.0912, 0,
        0, 0, 0, 1
      ]
    });
    renders[1] = new H3DU.RenderPass(H3DU.Batch3D.forFilter(scene, fbo, currentFilter));
  }
}

/* exported linkvignette */
function linkvignette() {
  "use strict";
  if(scene) {
    currentFilter = makeVignette();
    currentFilter.setUniforms({
      "radius":0.75,
      "softness":0.45,
      "opacity":0.5
    });
    renders[1] = new H3DU.RenderPass(H3DU.Batch3D.forFilter(scene, fbo, currentFilter));
  }
}
/* exported linksepia2 */
function linksepia2() {
  "use strict";
  if(scene) {
    currentFilter = makeColorMatrix();
    currentFilter.setUniforms({
      "colorMatrix":[
        0.299, 0.2841, 0.2452, 0,
        0.587, 0.5577, 0.4813, 0,
        0.114, 0.1083, 0.0935, 0,
        0, 0, 0, 1
      ]
    });
    renders[1] = new H3DU.RenderPass(H3DU.Batch3D.forFilter(scene, fbo, currentFilter));
  }
}

/* exported link1a */
function link1a() {
  "use strict";
  if(scene) {
    subScene.setProjectionMatrix(H3DU.Math.mat4ortho(-1, 1, -1, 1, -10, 10));
    subScene.removeShape(platonic);
    subScene.addShape(blinds);
  }
}
function link2a() {
  "use strict";
  if(scene) {
    subScene.orthoAspect(-20, 20, -10, 10, -20, 20);
    subScene.removeShape(blinds);
    subScene.addShape(platonic);
  }
}
/* exported link2 */
function link2() {
  "use strict";
  if(scene) {
    currentFilter = makeColorMatrix();
    currentFilter.setUniforms({
      "colorMatrix":[
        -1, 0, 0, 0,
        0, -1, 0, 0,
        0, 0, -1, 0,
        1, 1, 1, 1
      ]
    });
    renders[1] = new H3DU.RenderPass(H3DU.Batch3D.forFilter(scene, fbo, currentFilter));
  }
}

/* exported link3 */
function link3() {
  "use strict";
  if(scene) {
    currentFilter = makeWave();
    renders[1] = new H3DU.RenderPass(H3DU.Batch3D.forFilter(scene, fbo, currentFilter));
  }
}

/* exported link4 */
function link4() {
  "use strict";
  if(scene) {
    currentFilter = makeKernelMatrix(scene);
    setKernelMatrix(currentFilter, [1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9]);
    renders[1] = new H3DU.RenderPass(H3DU.Batch3D.forFilter(scene, fbo, currentFilter));
  }
}
/* exported link5 */
function link5() {
  "use strict";
  if(scene) {
    currentFilter = makePixelate();
    renders[1] = new H3DU.RenderPass(H3DU.Batch3D.forFilter(scene, fbo, currentFilter));
  }
}
/* exported link6 */
function link6() {
  "use strict";
  if(scene) {
    currentFilter = makeWaterpaint();
    renders[1] = new H3DU.RenderPass(H3DU.Batch3D.forFilter(scene, fbo, currentFilter));
  }
}
/* exported link7 */
function link7() {
  "use strict";
  if(scene) {
    currentFilter = makeMirror(scene);
    renders[1] = new H3DU.RenderPass(H3DU.Batch3D.forFilter(scene, fbo, currentFilter));
  }
}
/* exported link8 */
function link8() {
  "use strict";
  if(scene) {
    currentFilter = makeKernelMatrix(scene);
    setKernelMatrix(currentFilter, [0, 1, 0, 1, -4, 1, 0, 1, 0]);
    renders[1] = new H3DU.RenderPass(H3DU.Batch3D.forFilter(scene, fbo, currentFilter));
  }
}

/* exported link9 */
function link9() {
  "use strict";
  if(scene) {
    currentFilter = makeColorMatrix();
    currentFilter.setUniforms({
      "colorMatrix":[
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        0.3, 0, 0, 1
      ]
    });
    renders[1] = new H3DU.RenderPass(H3DU.Batch3D.forFilter(scene, fbo, currentFilter));
  }
}

/* exported link9a */
function link9a() {
  "use strict";
  if(scene) {
    currentFilter = makeColorMatrix();
    currentFilter.setUniforms({
      "colorMatrix":[
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        0, 0, 0.3, 1
      ]
    });
    renders[1] = new H3DU.RenderPass(H3DU.Batch3D.forFilter(scene, fbo, currentFilter));
  }
}

/* exported link10 */
function link10() {
  "use strict";
  if(scene) {
    currentFilter = makeWarp(scene);
    renders[1] = new H3DU.RenderPass(H3DU.Batch3D.forFilter(scene, fbo, currentFilter));
  }
}
function linknone() {
  "use strict";
  if(scene) {
    currentFilter = null;
    renders[1] = new H3DU.RenderPass(H3DU.Batch3D.forFilter(scene, fbo, currentFilter));
  }
}

function platonicGroup() {
  "use strict";
  var size = 4.5;
  var group = new H3DU.ShapeGroup();
  var mesh = H3DU.Polyhedra.hexahedron(size);
  group.addShape(
    new H3DU.Shape(mesh).setColor("red").setPosition(-15, 5, 0));
  mesh = H3DU.Polyhedra.octahedron(size);
  group.addShape(
    new H3DU.Shape(mesh).setColor("blue").setPosition(-5, 5, 0));
  mesh = H3DU.Polyhedra.icosahedron(size);
  group.addShape(
    new H3DU.Shape(mesh).setColor("lime").setPosition(5, 5, 0));
  mesh = H3DU.Polyhedra.tetrahedron(size);
  group.addShape(
    new H3DU.Shape(mesh).setColor("darkorange").setPosition(15, 5, 0));
  mesh = H3DU.Polyhedra.dodecahedron(size);
  group.addShape(
    new H3DU.Shape(mesh).setColor("yellow").setPosition(-15, -5, 0));
  return group;
}

function blindsGroup() {
  "use strict";
  var color = true;
  var oddrow = false;
  var group = new H3DU.ShapeGroup();
  for(var x = -100; x < 100; x += 20) {
    color = oddrow;
    for(var y = -100; y < 100; y += 20) {
      var xx = (x / 100 + 1) / 2;
      var yy = (y / 100 + 1) / 2;
      var mesh = H3DU.Meshes.createPlane(0.2, 0.2);
      group.addShape(new H3DU.Shape(mesh)
      .setColor(color ? [0, xx, yy] : [1, 0.7, 0.7])
      .setPosition(x / 100 + 0.1, y / 100 + 0.1, 0));
      color = !color;
    }
    oddrow = !oddrow;
  }
  return group;
}

  // Create the 3D scene; find the HTML canvas and pass it
  // to Scene3D.
scene = new H3DU.Scene3D(document.getElementById("canvas"));
fbo = new H3DU.FrameBufferInfo(scene.getWidth(), scene.getHeight());
scene.setClearColor("white");
subScene = new H3DU.Batch3D();
subScene.getLights().setBasic();
renders = [
  new H3DU.RenderPass(subScene, {"frameBuffer":fbo}),
  new H3DU.RenderPass(H3DU.Batch3D.forFilter(scene, fbo))
];
var fc = new H3DU.FrameCounterDiv();
blinds = blindsGroup(subScene);
platonic = platonicGroup(subScene);
linknone();
link2a();
var rotation = [0, 0, 0];
var timer = {};
var c = [1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2];
var rot = 0;
H3DU.renderLoop(function(time) {
  "use strict";
  var i;
  fbo.resize(scene.getWidth(), scene.getHeight());
  if(currentFilter) {
    currentFilter.setUniforms({
      "coarseness":c[Math.floor(c.length * H3DU.getTimePosition(timer, time, 1500))],
      "time":100 * H3DU.getTimePosition(timer, time, 5000),
      "t":1.0,
      "globalTime":Math.PI * 2 * H3DU.getTimePosition(timer, time, 5000)
    });
  }
  for(i = 0; i < blinds.shapeCount(); i++) {
    blinds.getShape(i).setQuaternion(
      H3DU.Math.quatFromAxisAngle(rot, 0, 1, 0));
  }
  for(i = 0; i < platonic.shapeCount(); i++) {
    platonic.getShape(i).setQuaternion(
      H3DU.Math.quatFromTaitBryan(rotation));
  }
  rot = 360 * H3DU.getTimePosition(timer, time, 2000);
  rotation[0] = 360 * H3DU.getTimePosition(timer, time, 6000);
  rotation[1] = 360 * H3DU.getTimePosition(timer, time, 12000);
  scene.render(renders);
  fc.update();
});
// -->
</script>
</body>
